package utils

import (
	"encoding/binary"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	"probeping/src/model"
	"time"

	_ "github.com/glebarez/go-sqlite"
)

func GetRoot() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal("Get Root Path Error:", err)
	}
	if !IsExist(dir + string(os.PathSeparator) + "conf" + string(os.PathSeparator) + ConfigFileName) {
		dir = filepath.Dir(dir)
	}
	return dir
}

func IsExist(fp string) bool {
	_, err := os.Stat(fp)
	return err == nil || os.IsExist(err)
}

// 获取可用端口
func GetAvailablePort() (int, error) {
	address, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", "0.0.0.0"))
	if err != nil {
		return 0, err
	}

	listener, err := net.ListenTCP("tcp", address)
	if err != nil {
		return 0, err
	}

	defer listener.Close()
	return listener.Addr().(*net.TCPAddr).Port, nil

}

// 判断端口是否可以（未被占用）
func IsPortAvailable(port int) bool {
	address := fmt.Sprintf("%s:%d", "0.0.0.0", port)
	listener, err := net.Listen("tcp", address)
	if err != nil {
		log.Printf("port %s is taken: %s", address, err)
		return false
	}

	defer listener.Close()
	return true
}

func GetAvailableInterfaces() ([]model.Interface, error) {
	var intfs []model.Interface
	var routeIps []net.IPNet

	routes, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}

	for _, route := range routes {
		inet, ok := route.(*net.IPNet)
		if ok && inet.IP.To4() != nil && !inet.IP.IsLinkLocalUnicast() && !inet.IP.IsLoopback() {
			ipAddr := net.IPAddr{
				IP:   inet.IP,
				Zone: "", // 可选的接口域（zone）
			}
			_, err := RunPing(&ipAddr, 100*time.Microsecond, 64, 1)
			if err == nil {
				routeIps = append(routeIps, *inet)
			}
		}
	}

	interfaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	for _, inter := range interfaces {
		if (inter.Flags&net.FlagUp) != 0 && (inter.Flags&net.FlagLoopback) == 0 {
			addrs, err := inter.Addrs()
			if err != nil {
				continue
			}
			for _, addr := range addrs {

				ipNet, ok := addr.(*net.IPNet)
				if !ok {
					continue
				}

				isContain := false
				for _, routeIp := range routeIps {
					if routeIp.IP.Equal(ipNet.IP) {
						isContain = true
					}

				}
				if isContain {
					intf := model.Interface{}
					intf.Name = inter.Name
					intf.Ip = *ipNet

					intfs = append(intfs, intf)
				}
			}
		}
	}

	return intfs, nil
}

func GetIpRange(ip net.IPNet) []net.IP {

	// Calculate IP and mask
	ipInt := ipToInt(ip.IP[12:])
	maskInt := ipToInt(ip.Mask)

	// Calculate network address
	networkInt := ipInt & maskInt
	network := intToIP(networkInt)

	// Calculate broadcast address
	broadcastInt := ipInt | ^maskInt
	broadcast := intToIP(broadcastInt)

	// Generate all IPs in the range
	var ips []net.IP
	tempIp := network
	incrementIP(tempIp) // Skip .0
	for ; tempIp.String() != broadcast.String(); incrementIP(tempIp) {
		ips = append(ips, copyIP(tempIp))
	}

	return ips
}

func ipToInt(ip []byte) uint32 {
	return binary.BigEndian.Uint32(ip)
}

func intToIP(ipInt uint32) net.IP {
	ip := make(net.IP, 4)
	binary.BigEndian.PutUint32(ip, ipInt)
	return ip
}

func incrementIP(ip net.IP) {
	for i := len(ip) - 1; i >= 0; i-- {
		ip[i]++
		if ip[i] > 0 {
			break
		}
	}
}

func copyIP(x net.IP) net.IP {
	y := make(net.IP, len(x))
	copy(y, x)
	return y
}
